package com.ma.dp.subsequence;

import java.util.concurrent.ForkJoinPool;

/**
 * @ClassName Solution712
 * @Author: mayongqiang
 * @DATE 2022/4/6 10:54
 * @Description: 给定两个字符串s1 和 s2，返回 使两个字符串相等所需删除字符的 ASCII 值的最小和 。
 */
public class Solution712 {
    /*  输入: s1 = "sea", s2 = "eat"
        输出: 231
            解释: 在 "sea" 中删除 "s" 并将 "s" 的值(115)加入总和。在 "eat" 中删除 "t" 并将 116 加入总和。
            结束时，两个字符串相等，115 + 116 = 231 就是符合条件的最小和     */

    public static void main(String[] args) {
        System.out.println(minimumDeleteSum("sea", "eat"));
        System.out.println(minimumDeleteSum1("sea", "eat"));
    }

    /*
    动态规划解法：
        子序列问题，穷举出所有可能的结果并不显示     动态规划-->穷举+剪枝
        套路：dp table 二维数组  两个维度表示给定两数组的索引
            1、明确dp数组的含义
                dp[i][j]代表 子问题s1[0..i]与s2[0..j] 他们的最小和
            2、定义base case索引为0的行与列表示空串 子序列自然为空
                dp[0][...]  str1为空  最小和为 str2的所有字符ASCII码和
                dp[...][0]  str1为空  最小和为 str1的所有字符ASCII码和
            3、拟状态转移方程 “当前问题与子问题的关系”
                if(str[i-1]==str[j-1])
                    // 不需要删除，与上一步结果一致
                    dp[i][j] = dp[i-1][j-1] ;
                else
                    // str1[i-1]或者str2[j-1] 返回使结果最小的
                    dp[i][j] = min(dp[i-1][j]+str[i-1],dp[i][j-1]+str[j-1]);
     */
    public static int minimumDeleteSum1(String s1, String s2) {
        int m = s1.length(), n = s2.length();
        int[][] dp = new int[m + 1][n + 1];
        for (int i = 1; i <= m; i++) {
            dp[i][0] = dp[i - 1][0] + s1.charAt(i - 1);
        }
        for (int j = 1; j <= n; j++) {
            dp[0][j] = dp[0][j - 1] + s2.charAt(j - 1);
        }
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1];
                } else {
                    dp[i][j] = Math.min(dp[i - 1][j] + s1.charAt(i - 1), dp[i][j - 1] + s2.charAt(j - 1));
                }
            }
        }
        return dp[m][n];
    }

    /* 1、套用LCS巧妙解法
     * 改造LCS 将其输出改造为 最长公共子序列的ASCII码和
     * 那么使两个字符串相等所需删除字符的 ASCII 值的最小和 就是两字符串的ASCII码和减去二倍的最长公共子序列的ASCII码和
     * */
    public static int minimumDeleteSum(String s1, String s2) {
        int m = s1.length(), n = s2.length();
        int res = 0;
        for (int i = 0; i < m; i++) {
            res += s1.charAt(i);
        }
        for (int i = 0; i < m; i++) {
            res += s2.charAt(i);
        }

        int[][] dp = new int[m + 1][n + 1];
        for (int i = 1; i <= m; i++) {
            for (int j = 1; j <= n; j++) {
                if (s1.charAt(i - 1) == s2.charAt(j - 1)) {
                    dp[i][j] = dp[i - 1][j - 1] + s1.charAt(i - 1);
                } else {
                    dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
                }
            }
        }

        return res - 2 * dp[m][n];
    }


}
